/*
    Heart of Gold engine management system
    Copyright (C) 2004 Heart of Gold development team

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package com.heartOfGold.view;

/*
 * The Engine Configuration page for the megasquirt HOG_Component
 * @author Ryan Higgins
 * @date May 2004
 * @version %I%, %G%
 */
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.GridLayout;
import java.awt.Dimension;

import javax.swing.JButton;
import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
//import javax.swing.JLabel;
import javax.swing.JComboBox;
import javax.swing.JTextField;
import javax.swing.Box;

import com.heartOfGold.controller.Fuel;
import com.heartOfGold.model.megasquirtECU.UnsignedByte;

import java.io.*;

/*
 */

public class FuelEngine extends JPanel implements ActionListener {
    private UnsignedByte config11, config12, config13;
    private int divider = 0;
    private double reqFuelMult = 0;

    private JPanel buttons;
    private JButton get, send, done;
    private JTextField calculated, sent, openField, battField, limitField,
            threshField, idleField;
    private String /* calc, */sentReq, open, batt, limit, thresh, idle;
    private JComboBox stratList, stageList, squirtList, strokeList,
            cylinderList, portList, numberList, mapList, fireList, o2List,
            baroList;
    private int strat, stage, /* squirt, */stroke, cylinder, port, number, map,
            fire, o2, baro;
    private FuelMaster master;
    private Fuel model;
    private Dimension combos;
    private Dimension labels;
    private Dimension labels2;

    //TODO break this method into smaller chunks
    public FuelEngine(final FuelMaster m,
            final Fuel mod) {
        model = mod;
        master = m;
        Dimension fieldSize = new Dimension(45, 25);
        setBorder(BorderFactory.createTitledBorder(
                "Engine and Injector Setup"));

        JLabel myHack = new JLabel("Battery Voltage Correction (ms/V)");
        labels2 = myHack.getPreferredSize();

        // setup the required fuel box
        JButton req = new JButton("Required Fuel");
        req.addActionListener(this);
        calculated = new JTextField();
        calculated.addActionListener(this);
        calculated.setActionCommand("calc");
        sent = new JTextField();
        calculated.setPreferredSize(fieldSize);
        sent.setPreferredSize(fieldSize);
        calculated.setHorizontalAlignment(JTextField.CENTER);
        sent.setHorizontalAlignment(JTextField.CENTER);
        sent.setEditable(false);
        JPanel reqFuel = new JPanel();
        reqFuel.setBorder(BorderFactory
                .createTitledBorder("Calculate Required Fuel"
                        + " - One Cylinder(ms)"));
        reqFuel.add(req);
        reqFuel.add(calculated);
        reqFuel.add(sent);

        // setup the injector opening time box
        JLabel openTime = new JLabel("Injector Open Time (ms)");
        openTime.setPreferredSize(labels2);
        openField = new JTextField();
        openField.setPreferredSize(fieldSize);
        openField.setHorizontalAlignment(JTextField.CENTER);
        JPanel openStuff = new JPanel();
        openStuff.add(openTime);
        openStuff.add(openField);
        JLabel battVolt = new JLabel("Battery Voltage Correction (ms/V)");
        battVolt.setPreferredSize(labels2);
        battField = new JTextField();
        battField.setPreferredSize(fieldSize);
        battField.setHorizontalAlignment(JTextField.CENTER);
        JPanel battStuff = new JPanel();
        battStuff.add(battVolt);
        battStuff.add(battField);
        JPanel openingTime = new JPanel(new GridLayout(2, 1));
        openingTime.add(openStuff);
        openingTime.add(battStuff);
        openingTime.setBorder(BorderFactory
                .createTitledBorder("Injector Opening Control"));

        // setup injector current control box
        JLabel currentLimit = new JLabel("PWM Current Limit (%)");
        currentLimit.setPreferredSize(labels2);
        limitField = new JTextField();
        limitField.setPreferredSize(fieldSize);
        limitField.setHorizontalAlignment(JTextField.CENTER);
        JPanel limitStuff = new JPanel();
        limitStuff.add(currentLimit);
        limitStuff.add(limitField);
        JLabel threshold = new JLabel("PWM Time Threshold (ms)");
        threshold.setPreferredSize(labels2);
        threshField = new JTextField();
        threshField.setPreferredSize(fieldSize);
        threshField.setHorizontalAlignment(JTextField.CENTER);
        JPanel threshStuff = new JPanel();
        threshStuff.add(threshold);
        threshStuff.add(threshField);
        JPanel current = new JPanel(new GridLayout(2, 1));
        current.add(limitStuff);
        current.add(threshStuff);
        current.setBorder(BorderFactory
                .createTitledBorder("Injector Current Control"));

        // setup the Fast Idle Control
        JLabel fastIdle = new JLabel("Fast Idle Threshold (F)");
        fastIdle.setPreferredSize(labels2);
        idleField = new JTextField();
        idleField.setPreferredSize(fieldSize);
        idleField.setHorizontalAlignment(JTextField.CENTER);
        JPanel idlePanel = new JPanel();
        idlePanel.add(fastIdle);
        idlePanel.add(idleField);
        idlePanel.setBorder(BorderFactory
                .createTitledBorder("Fast Idle Control"));

        // begining of JComboBoxes
        // setup the Fuel Injection Control Strategy
        String[] strategy = {"Speed-Density", "Alpha-N"};
        stratList = new JComboBox(strategy);
        stratList.setSelectedIndex(0);
        JPanel fuelStrategy = new JPanel();
        fuelStrategy.add(stratList);
        fuelStrategy.setBorder(BorderFactory
                .createTitledBorder("Fuel Injection Control Strategy"));

        // setup the Injector Staging
        String[] stages = {"Simultaneous", "Alternate"};
        stageList = new JComboBox(stages);
        stageList.addActionListener(this);
        stageList.setActionCommand("calc");
        stageList.setSelectedIndex(0);
        JLabel stageLabel = new JLabel("Injector Channel Staging");
        JPanel stagePanel = new JPanel();
        stagePanel.add(stageLabel);
        stagePanel.add(stageList);

        // setup standard dimensions from the largest things
        combos = stageList.getPreferredSize();
        labels = stageLabel.getPreferredSize();

        // setup the squirts per cycle
        String[] squirts = {"1 Squirt", "2 Squirts", "3 Squirts", "4 Squirts",
                "5 Squirts", "6 Squirts", "7 Squirts", "8 Squirts"};
        squirtList = new JComboBox(squirts);
        squirtList.addActionListener(this);
        squirtList.setActionCommand("calc");
        squirtList.setSelectedIndex(0);
        JLabel squirtLabel = new JLabel("Injections Per Cycle");
        JPanel squirtPanel = new JPanel();
        squirtLabel.setPreferredSize(labels);
        squirtList.setPreferredSize(combos);
        squirtPanel.add(squirtLabel);
        squirtPanel.add(squirtList);

        // setup the Engine Stroke
        String[] engineStroke = {"Four Stroke", "Two Stroke"};
        strokeList = new JComboBox(engineStroke);
        strokeList.setSelectedIndex(0);
        JLabel strokeLabel = new JLabel("Engine Stroke Type");
        JPanel strokePanel = new JPanel();
        strokeLabel.setPreferredSize(labels);
        strokeList.setPreferredSize(combos);
        strokePanel.add(strokeLabel);
        strokePanel.add(strokeList);

        // setup the number of cylinders
        String[] cylinders = {"1", "2", "3", "4", "5", "6", "7", "8", "9",
                "10", "11", "12"};
        cylinderList = new JComboBox(cylinders);
        cylinderList.addActionListener(this);
        cylinderList.setActionCommand("calc");
        cylinderList.setSelectedIndex(0);
        JLabel cylinderLabel = new JLabel("Number Of Cylinders");
        JPanel cylinderPanel = new JPanel();
        cylinderLabel.setPreferredSize(labels);
        cylinderList.setPreferredSize(combos);
        cylinderPanel.add(cylinderLabel);
        cylinderPanel.add(cylinderList);

        // setup the injection port type
        String[] ports = {"Multi-Port", "Throttle Body"};
        portList = new JComboBox(ports);
        portList.setSelectedIndex(0);
        JLabel portLabel = new JLabel("Injection Port Type");
        JPanel portPanel = new JPanel();
        portLabel.setPreferredSize(labels);
        portList.setPreferredSize(combos);
        portPanel.add(portLabel);
        portPanel.add(portList);

        // setup the number of injectors
        String[] numbers = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
                "11", "12"};
        numberList = new JComboBox(numbers);
        numberList.addActionListener(this);
        numberList.setActionCommand("calc");
        numberList.setSelectedIndex(0);
        JLabel numberLabel = new JLabel("Number Of Injectors");
        JPanel numberPanel = new JPanel();
        numberLabel.setPreferredSize(labels);
        numberList.setPreferredSize(combos);
        numberPanel.add(numberLabel);
        numberPanel.add(numberList);

        // setup the MAP type
        String[] maps = {"115 KPa", "250 KPa"};
        mapList = new JComboBox(maps);
        mapList.setSelectedIndex(0);
        JLabel mapLabel = new JLabel("MAP Type");
        JPanel mapPanel = new JPanel();
        mapLabel.setPreferredSize(labels);
        mapList.setPreferredSize(combos);
        mapPanel.add(mapLabel);
        mapPanel.add(mapList);

        // setup Even Fire or Odd Fire
        String[] sparkFire = {"Even Fire", "Odd Fire"};
        fireList = new JComboBox(sparkFire);
        fireList.setSelectedIndex(0);
        JLabel fireLabel = new JLabel("Engine Type");
        JPanel firePanel = new JPanel();
        fireLabel.setPreferredSize(labels);
        fireList.setPreferredSize(combos);
        firePanel.add(fireLabel);
        firePanel.add(fireList);

        // setup O2 sensor
        String[] o2Type = {"Narrow Band", "DIY Wide Band"};
        o2List = new JComboBox(o2Type);
        o2List.setSelectedIndex(0);
        JLabel o2Label = new JLabel("Oxygen Sensor Type");
        JPanel o2Panel = new JPanel();
        o2Label.setPreferredSize(labels);
        o2List.setPreferredSize(combos);
        o2Panel.add(o2Label);
        o2Panel.add(o2List);

        // setup Barometric Correction enabled
        String[] baroCorrect = {"Disabled", "Enabled"};
        baroList = new JComboBox(baroCorrect);
        baroList.setSelectedIndex(0);
        JLabel baroLabel = new JLabel("Barometric Correction");
        JPanel baroPanel = new JPanel();
        baroLabel.setPreferredSize(labels);
        baroList.setPreferredSize(combos);
        baroPanel.add(baroLabel);
        baroPanel.add(baroList);

        // setup buttons
        buttons = new JPanel();
        get = new JButton("Get From Controller");
        send = new JButton("Send To Controller");
        done = new JButton("Done");
        get.addActionListener(this);
        send.addActionListener(this);
        done.addActionListener(this);
        buttons.add(get);
        buttons.add(send);
        buttons.add(done);
        // buttons.setBorder(BorderFactory.createEtchedBorder());

        // make the injection control panel and add its pieces
        JPanel injectionControl = new JPanel(new GridLayout(10, 1));
        injectionControl.setBorder(BorderFactory
                .createTitledBorder("InjectionControl"));
        injectionControl.add(squirtPanel);
        injectionControl.add(stagePanel);
        injectionControl.add(strokePanel);
        injectionControl.add(cylinderPanel);
        injectionControl.add(portPanel);
        injectionControl.add(numberPanel);
        injectionControl.add(mapPanel);
        injectionControl.add(firePanel);
        injectionControl.add(o2Panel);
        injectionControl.add(baroPanel);

        Box alignV = Box.createVerticalBox();

        alignV.add(reqFuel);
        alignV.add(openingTime);
        alignV.add(current);
        alignV.add(idlePanel);
        alignV.add(fuelStrategy);

        Box alignH = Box.createHorizontalBox();
        alignH.add(alignV);
        alignH.add(injectionControl);

        Box alignAll = Box.createVerticalBox();
        alignAll.add(alignH);
        alignAll.add(buttons);

        add(alignAll);

    }

    public final void actionPerformed(final ActionEvent ae) {
        String command = ae.getActionCommand();

        if (command.equals("calc")) {
            // Equation for determining Req_fuel_download from Req_fuel:
            //
            // REQ_FUEL_DL = reqFuel * (B * N)/NINJ
            //
            // B = 1 if simultaneous, 2 if alternating
            // N = divider_number = ncyl/number_of_squirts
            // NINJ = number of injectors
            String theValue = calculated.getText();
            if (!theValue.equals("")) {
                sent.setText("" + Double.parseDouble(calculated.getText())
                        * (double) (stageList.getSelectedIndex() + 1)
                        * (double) (cylinderList.getSelectedIndex() + 1)
                        / (double) (squirtList.getSelectedIndex() + 1)
                        / (double) (numberList.getSelectedIndex() + 1));
            }
            return;
        }
        if (command.equals("Get From Controller")) {
            try {
                model.getConstants();
            } catch (IOException e) {
            }
            return;
        }

        if (command.equals("Send To Controller")) {
            try {
                addChange();
                model.writeChanges();
                model.burn();
            } catch (IOException e) {
            }
            return;
        }

        if (command.equals("Done")) {
            master.switchScreen("Main");
            return;
        }

        JOptionPane.showMessageDialog(this,
                "This function is not yet implemented", "Not Implemented",
                JOptionPane.INFORMATION_MESSAGE);
    }

    public final void update(final int[] constants) {
        config11 = new UnsignedByte(constants[116]);
        config12 = new UnsignedByte(constants[117]);
        config13 = new UnsignedByte(constants[118]);
        divider = constants[91];
        reqFuelMult = ((double) config12.getUpperNibble() + 1)
                / (divider * (constants[92] + 1));
        calculated.setText("" + (((double) constants[90] / 10) * reqFuelMult));
        // calc = new String(calculated.getText());
        sent.setText("" + (double) constants[90] / 10);
        sentReq = new String(sent.getText());
        openField.setText("" + (double) constants[93] / 10);
        open = new String(openField.getText());
        battField.setText("" + (double) constants[97] / 60);
        batt = new String(battField.getText());
        limitField.setText("" + constants[95]);
        limit = new String(limitField.getText());
        threshField.setText("" + ((double) constants[96] / 10));
        thresh = new String(threshField.getText());
        idleField.setText("" + (constants[121] - 40));
        idle = new String(idleField.getText());

        stratList.setSelectedIndex(config13.getBit(2));
        strat = stratList.getSelectedIndex();
        stageList.setSelectedIndex(constants[92]);
        stage = stageList.getSelectedIndex();
        squirtList.setSelectedIndex(((config11.getUpperNibble()
                + 1) / divider) - 1);
        // squirt = squirtList.getSelectedIndex();
        strokeList.setSelectedIndex(config11.getBit(2));
        stroke = strokeList.getSelectedIndex();
        cylinderList.setSelectedIndex(config11.getUpperNibble());
        cylinder = cylinderList.getSelectedIndex();
        portList.setSelectedIndex(config11.getBit(3));
        port = portList.getSelectedIndex();
        numberList.setSelectedIndex(config12.getUpperNibble());
        number = numberList.getSelectedIndex();
        mapList.setSelectedIndex(config11.getBits(0, 2));
        map = mapList.getSelectedIndex();
        fireList.setSelectedIndex(config13.getBit(0));
        fire = fireList.getSelectedIndex();
        o2List.setSelectedIndex(config13.getBit(1));
        o2 = o2List.getSelectedIndex();
        baroList.setSelectedIndex(config13.getBit(3));
        baro = baroList.getSelectedIndex();
        return;
    }

    public final void addChange() {
        boolean changedConfig11 = false;
        boolean changedConfig12 = false;
        boolean changedConfig13 = false;
        int toSend = 0;
        int nDivider = divider;
        nDivider = (cylinderList.getSelectedIndex() + 1)
                / (squirtList.getSelectedIndex() + 1);

        if (nDivider != divider) {
            toSend = nDivider;
            model.addChange(91, toSend);
        }
        if (!sentReq.equals(sent.getText())) {
            toSend = (int) (Double.parseDouble(sent.getText()) * 10);
            model.addChange(90, toSend);
        }
        if (!open.equals(openField.getText())) {
            toSend = (int) (Double.parseDouble(openField.getText()) * 10);
            model.addChange(93, toSend);
        }
        if (!batt.equals(battField.getText())) {
            toSend = (int) (Double.parseDouble(battField.getText()) * 60);
            model.addChange(97, toSend);
        }
        if (!limit.equals(limitField.getText())) {
            toSend = (int) (Double.parseDouble(calculated.getText()));
            model.addChange(95, toSend);
        }
        if (!thresh.equals(threshField.getText())) {
            toSend = (int) (Double.parseDouble(threshField.getText()) * 10);
            model.addChange(96, toSend);
        }
        if (!idle.equals(idleField.getText())) {
            toSend = (int) (Double.parseDouble(idleField.getText()) + 40);
            model.addChange(121, toSend);
        }
        if (strat != stratList.getSelectedIndex()) {
            config13.setBit(2, stratList.getSelectedIndex());
            changedConfig13 = true;
        }
        if (stage != stageList.getSelectedIndex()) {
            toSend = stageList.getSelectedIndex();
            model.addChange(92, toSend);
        }
        if (stroke != strokeList.getSelectedIndex()) {
            config11.setBit(2, strokeList.getSelectedIndex());
            changedConfig11 = true;
        }
        if (cylinder != cylinderList.getSelectedIndex()) {
            config11.setUpperNibble(cylinderList.getSelectedIndex());
            changedConfig11 = true;
        }
        if (port != portList.getSelectedIndex()) {
            config11.setBit(3, portList.getSelectedIndex());
            changedConfig11 = true;
        }
        if (number != numberList.getSelectedIndex()) {
            config12.setUpperNibble(numberList.getSelectedIndex());
            changedConfig12 = true;
        }
        if (map != mapList.getSelectedIndex()) {
            config11.setBits(0, 2, mapList.getSelectedIndex());
            changedConfig12 = true;
        }
        if (fire != fireList.getSelectedIndex()) {
            config13.setBit(0, fireList.getSelectedIndex());
            changedConfig13 = true;
        }
        if (o2 != o2List.getSelectedIndex()) {
            config13.setBit(1, o2List.getSelectedIndex());
            changedConfig13 = true;
        }
        if (baro != baroList.getSelectedIndex()) {
            config13.setBit(3, baroList.getSelectedIndex());
            changedConfig13 = true;
        }
        // if any ChangeConfigs are true add them
        if (changedConfig11) {
            model.addChange(116, config11.getValue());
        }
        if (changedConfig12) {
            model.addChange(117, config12.getValue());
        }
        if (changedConfig13) {
            model.addChange(118, config13.getValue());
        }
        try {
            model.writeChanges();
        } catch (IOException e) {
        }
        return;
    }
    /*
     * private boolean checkRange(String toCheck, double lo, double hi, String
     * name){ double value = Double.parseDouble(toCheck); if((value < lo) ||
     * (value > hi)){ JOptionPane.showMessageDialog(this, name +
     * " must have a value between " + lo + " and " + hi + "!",
     * "Danger! Warning! Houston, we have a problem...",
     * JOptionPane.WARNING_MESSAGE); return false; } return true; }
     */
}
